import json

from src.utils import matrix_utils
import numpy as np


class HeapInfo():
    def __init__(self,features=None,
                 heap_index=None,
                 relative_heap_index=None,
                 max_relative_heap_index=None,
                 max_relative_heap_inner_index = None,
                 relative_heap_inner_index = None,
                 **kwargs):
        self.heap_features = features
        self.heap_start = None
        self.heap_end = None
        if heap_index:
            self.heap_start = heap_index[0]
            self.heap_end = heap_index[-1]
        self.heap_index = heap_index
        self.max_relative_heap_index = max_relative_heap_index
        self.max_relative_heap_inner_index = max_relative_heap_inner_index
        self.relative_heap_index = relative_heap_index
        self.relative_heap_inner_index = relative_heap_inner_index


    def set_heap_relatetive_second(self,relative_list,relative_innner_list):
        '''
        将和当前heap相似的 heap的index记录下来
        :param relative_index:
        :return:
        '''
        try:
            relative_index  = [i.heap_index for i in relative_list if i != None]
            self.relative_heap_index =  relative_index
            self.relative_heap_inner_index = relative_innner_list
        except Exception as e :
            pass


    def init_from_dict(self,dataDict):
        self.heap_end = dataDict['heap_end']
        self.heap_start = dataDict['heap_start']
        self.heap_index = list(range(self.heap_start,self.heap_end +1))
        self.max_relative_heap_index = dataDict['max_relative_heap_index']
        self.relative_heap_index = dataDict['relative_heap_index']

        self.max_relative_heap_inner_index = dataDict['max_relative_heap_inner_index']
        self.relative_heap_inner_index = dataDict['relative_heap_inner_index']


    def caculate_heap_similary(self,videoHeap):
        '''
        计算输入的videoHeap和当前videoHeap的相似度
        :param videoHeap:
        :return:
        '''
        similary_score = []
        for feature in self.heap_features:
            feature = np.array(feature)
            feature = feature.reshape((1, -1))
            single_result = matrix_utils.consine_similarity(feature, videoHeap.heap_features)
            similary_score.extend(single_result)
        return sum(similary_score) / len(similary_score)

    def __str__(self):
        return json.dumps({
            "heap_start":self.heap_start
            ,'heap_end':self.heap_end
            ,"heap_index":self.heap_index
            ,'relative_heap_index':self.relative_heap_index
            ,"relative_heap_inner_index":self.relative_heap_inner_index
            ,"max_relative_heap_index":self.max_relative_heap_index
            ,"max_relative_heap_inner_index":self.max_relative_heap_inner_index

        })



class VideoHeapsInfo(object):
    def __init__(self,features =None,heap_thredholder:float = 0.8,grap_sencond=60,top_num = 6,**kwargs):
        self.grap_sencond = grap_sencond
        self.top_num = top_num
        if features:
            self.features = features
            heapRelativeIndexs,heapRelativeFeatures = matrix_utils.caculary_heap_index_from_features(features
                                                                                                     ,heap_thredholder)
            self.heapInfoList = []
            for (relative_index,relativeFeatures)  in zip(heapRelativeIndexs,heapRelativeFeatures):
                self.heapInfoList.append(HeapInfo(relativeFeatures,relative_index))

            self.merge_one_index_heap()




    def merge_one_index_heap(self):
        merge_list = []
        for index,heap in enumerate(self.heapInfoList):
            if index == 0 or index == len(self.heapInfoList) -1:
                merge_list.append(heap)
                continue
            if len(heap.heap_index ) == 1:
                before_heap = self.heapInfoList[index - 1]
                after_heap = self.heapInfoList[index + 1]
                before_score = heap.caculate_heap_similary(before_heap)
                after_score = heap.caculate_heap_similary(after_heap)

                if before_score >= after_score:
                    before_heap.heap_end = before_heap.heap_end +1
                    before_heap.heap_index.append(before_heap.heap_end)
                else:
                    after_heap.heap_start = after_heap.heap_start - 1
                    after_heap.heap_index.insert(0,after_heap.heap_start)
            else:
                merge_list.append(heap)
        self.heapInfoList = merge_list





    def caculate_most_similary_videoHeap(self,videoHeap:HeapInfo) ->HeapInfo:
        '''
        计算和videoHeap最相似的heap
        :param videoHeap:
        :return:
        '''
        max_score = 0
        max_similary_heap = None
        for current_video_heap in self.heapInfoList:
            score = 0
            if len(current_video_heap.heap_index) >= len(videoHeap.heap_index):
                score = videoHeap.caculate_heap_similary(current_video_heap)
            if score > max_score:
                max_score = score
                max_similary_heap = current_video_heap
        return max_similary_heap

    def caculate_most_similary_videoHeap_New(self,videoHeapInfo,currentVideoHeap:HeapInfo):
        '''
        计算和videoHeap最相似的heap
        :param videoHeap:
        :return:
        '''
        result = []
        result_low = []

        for sub_video_heap in videoHeapInfo.heapInfoList:
            if len(sub_video_heap.heap_index) >= len(currentVideoHeap.heap_index):
                score = np.sum(self.similary_list[currentVideoHeap.heap_start:currentVideoHeap.heap_end +1,sub_video_heap.heap_start:sub_video_heap.heap_end + 1])
                score = score/(len(currentVideoHeap.heap_index) * len(sub_video_heap.heap_index))
                if score >= 0.6:
                    heap_in_similary_index,max_score = self.caculate_most_similary_start_index_in_heap(currentVideoHeap.heap_index,sub_video_heap.heap_index)
                    result.append((score,sub_video_heap,heap_in_similary_index,max_score))
        if result:
            result.sort(key=lambda x:x[3],reverse=True)
            result = result[:self.top_num]
        elif result_low:
            result_low.sort(key=lambda x: x[3], reverse=True)
            result = result_low[:3]
        else:
            raise ValueError("not similary score")

        return [i[1] for i in result],[i[2] for i in result]

    def caculate_most_similary_start_index_in_heap(self,current_heap,similary_heap):
        '''
        计算current_heap和similary_heap里面最相似的起始坐标
        :param current_heap:
        :param similary_heap:
        :return:
        '''
        max_score = -1
        max_similary_index = -1
        for i in range(len(similary_heap) - len(current_heap) +1):
            temp_score = np.sum(np.eye(len(current_heap)) * self.similary_list[current_heap[0]:current_heap[-1] +1,similary_heap[i]:similary_heap[i] + len(current_heap)])
            if temp_score > max_score:
                max_score = temp_score
                max_similary_index = similary_heap[i]

        return max_similary_index,max_score




    def caculate_most_similary_videoHeap_Bak(self,videoHeapInfo,currentVideoHeap:HeapInfo):
        '''
        计算和videoHeap最相似的heap
        :param videoHeap:
        :return:
        '''
        max_score = 0
        max_similary_heap = None

        second_score = 0
        second_similary_heap = None

        third_score = 0
        third_similary_heap = None

        forth_score = 0
        forth_similary_heap = None

        five_score = 0
        five_similary_heap = None

        for sub_video_heap in videoHeapInfo.heapInfoList:
            if len(sub_video_heap.heap_index) >= len(currentVideoHeap.heap_index):
                score = np.sum(self.similary_list[currentVideoHeap.heap_start:currentVideoHeap.heap_end +1,sub_video_heap.heap_start:sub_video_heap.heap_end + 1])
                score = score/(len(currentVideoHeap.heap_index) * len(sub_video_heap.heap_index))
                if score > max_score:
                    max_score = score
                    max_similary_heap = sub_video_heap
                elif score > second_score:
                    second_score = score
                    second_similary_heap = sub_video_heap
                elif score > third_score:
                    third_score = score
                    third_similary_heap = sub_video_heap
                elif score > forth_score:
                    forth_score = score
                    forth_similary_heap = sub_video_heap
                elif score > five_score:
                    five_score = score
                    five_similary_heap = sub_video_heap

        return [max_similary_heap,second_similary_heap,third_similary_heap,forth_similary_heap,five_similary_heap]
    def update_heap_relative_heaps(self,targetVideoHeaps):
        '''
        将输入的 targetVideoHeaps 里面和当前videoHeaps最相似的heap找出来，并跟新
        :param videoHeaps:
        :return:
        '''

        self.cacl_heap_similary_list(targetVideoHeaps.features)

        for heapInfo in self.heapInfoList:
            #max_similary_heap = targetVideoHeaps.caculate_most_similary_videoHeap(heapInfo)
            similary_list,similary_heap_innner_list = self.caculate_most_similary_videoHeap_New(targetVideoHeaps,heapInfo)
            heapInfo.set_heap_relatetive_second(similary_list,similary_heap_innner_list)

    def cacl_heap_similary_list(self,check_features):
        similary_list = matrix_utils.batch_cos(np.array(self.features),np.array(check_features))
        # for feature in self.features:
        #     similary_list.append(matrix_utils.consine_similarity(np.array([feature]),np.array(check_features)))
        self.similary_list = np.array(similary_list)



    def merge_single_heap(self):
        new_list = []
        for index, videoHeap in enumerate(self.heapInfoList):
            if videoHeap.max_relative_heap_index == None or videoHeap.relative_heap_index == []:
                videoHeap.max_relative_heap_index = videoHeap.relative_heap_index[0]
            if len(videoHeap.heap_index) == 1:
                before_index = []
                if index -1 >= 0:
                    before_index = self.heapInfoList[index - 1].max_relative_heap_index


                if before_index and min(videoHeap.max_relative_heap_index) - max(before_index)  <= 4 \
                        and min(videoHeap.max_relative_heap_index) >= max(before_index):
                    self.heapInfoList[index - 1].heap_index.append(videoHeap.heap_index[0])
                    self.heapInfoList[index + 1].heap_end += 1
                else:
                    self.heapInfoList[index + 1].heap_index.insert(0,min(self.heapInfoList[index + 1].heap_index) -1)
                    self.heapInfoList[index + 1].heap_start -= 1
            else:
                new_list.append(videoHeap)

        self.heapInfoList = new_list
        for index, i in enumerate(new_list):
            print("index:" + str(index))
            print( i.heap_index)
            print(i.max_relative_heap_index)
            print(i.relative_heap_index)
            print("")



    def ajust_same_closed_heap(self):
        for index, videoHeap in enumerate(self.heapInfoList):
            for i in range(1,4):
                if index + i < len(self.heapInfoList):
                    if set(videoHeap.max_relative_heap_index) == set(self.heapInfoList[index +i].max_relative_heap_index):
                        # 判断一下relatvie_heap_index里面是不是有符合的
                        before_node = self.heapInfoList[index +i -1]
                        after_node = None
                        if index + i + 1 < len(self.heapInfoList):
                            after_node = self.heapInfoList[index +i +1]

                        exist_heap = []
                        for temp_video_heap_index in self.heapInfoList[index +i].relative_heap_index:
                            if min(temp_video_heap_index) > max(before_node.max_relative_heap_index):
                                if after_node and min(after_node.max_relative_heap_index) > max(temp_video_heap_index):
                                    exist_heap = temp_video_heap_index
                                    break
                        if exist_heap:
                            self.heapInfoList[index + i].max_relative_heap_index = exist_heap
                        else:
                            left_haft = videoHeap.max_relative_heap_index[:int(len(videoHeap.max_relative_heap_index)/2)]
                            right_haft = videoHeap.max_relative_heap_index[int(len(videoHeap.max_relative_heap_index)/2):]
                            videoHeap.max_relative_heap_index = left_haft
                            self.heapInfoList[index + i].max_relative_heap_index = right_haft








    def merge_adjacent_node(self):
        '''
        依次判断相邻的结点是否有重合的片段，如果有，就合并成更大的片段
        :return:
        '''
        self.choose_best_heap()
        self.merge_single_heap()
        self.ajust_same_closed_heap()
        #self.resort_heap_index()

    def save_to_file(self,file_name=None):
        '''将结果保存到文件里面'''
        save_str  = json.dumps([str(i) for i in self.heapInfoList])
        with open(file_name,'w',encoding='utf-8') as fwriter:
            fwriter.write(save_str)


    def choose_best_heap(self):
        '''
        根据top
        :return:
        '''

        for current_heap in self.heapInfoList:
            current_heap.max_relative_heap_index = None
            current_heap.relative_heap_index = current_heap.relative_heap_index[:self.top_num]

        for i in range(len(self.heapInfoList)):
            if i == 17:
                j =1
            current_heap = self.heapInfoList[i]

            if current_heap.max_relative_heap_index == None:
                max_fix_able_num = 0
                max_relative_heap_index = []
                max_relative_heap_inner_index = -1
                for s_index,s_inner_max_index in zip(current_heap.relative_heap_index,current_heap.relative_heap_inner_index):
                    max_second_bottom = max(s_index) - self.grap_sencond
                    max_second_up = max_second_bottom + self.grap_sencond *2
                    fix_able_num = 0

                    for j in [1,2]:
                        if i - j >=0 :
                            relative_index = self.heapInfoList[i-j].max_relative_heap_index
                            if max(relative_index) >= max_second_bottom and min(relative_index) <= max_second_up:
                                fix_able_num  += 1
                                break



                    for j in range(1,4):
                        if i+j < len(self.heapInfoList):
                            for relative_index in self.heapInfoList[i+j].relative_heap_index:
                                if max(relative_index) >= max(s_index) and min(relative_index) <= max_second_up:
                                    fix_able_num += 1
                                    break
                            nearest_heaps = []
                            nearest_delta = 200
                            for relative_index in self.heapInfoList[i + j].relative_heap_index:
                                if max(relative_index) > max(s_index) and min(relative_index) <= max_second_up:
                                    if min(relative_index) - max(s_index) < nearest_delta:
                                        nearest_delta = min(relative_index) - max(s_index)
                                        nearest_heaps = relative_index
                            if nearest_heaps:
                                max_second_up = max(nearest_heaps) + self.grap_sencond


                    if fix_able_num > max_fix_able_num:
                        max_fix_able_num = fix_able_num
                        max_relative_heap_index = s_index
                        max_relative_heap_inner_index = s_inner_max_index

                if max_fix_able_num == 0:
                    temp_max_near = self.grap_sencond * 3 + 1
                    temp_max_near_index = []
                    temp_max_near_heap_inner_index = -1
                    if i - 1>=0:
                        before_heap_max = max(self.heapInfoList[i-1].max_relative_heap_index)
                        for temp,temp_heap_inner_index in zip(current_heap.relative_heap_index,current_heap.relative_heap_inner_index):
                            if min(temp) - before_heap_max > 0 and min(temp) - before_heap_max < temp_max_near:
                                temp_max_near = min(temp) - before_heap_max
                                temp_max_near_index = temp
                                temp_max_near_heap_inner_index = temp_heap_inner_index
                    if temp_max_near_index:
                        max_relative_heap_index = temp_max_near_index
                        max_relative_heap_inner_index = temp_max_near_heap_inner_index
                    else:
                        max_relative_heap_index = current_heap.relative_heap_index[0]
                        max_relative_heap_inner_index = current_heap.relative_heap_inner_index[0]
                current_heap.max_relative_heap_index = max_relative_heap_index
                current_heap.max_relative_heap_inner_index = max_relative_heap_inner_index

            current_target_heap = current_heap.max_relative_heap_index
            # 在确定了合适的index以后，把后面离这个最近的index给确定下来
            for j in range(1, 5):
                if i + j < len(self.heapInfoList):
                    if self.heapInfoList[i + j].max_relative_heap_index:
                        current_target_heap = self.heapInfoList[i + j].max_relative_heap_index
                        continue
                    else:
                        max_diff_second = self.grap_sencond
                        max_index = -1
                        for index, relative_index in enumerate(self.heapInfoList[i + j].relative_heap_index):
                            temp_diff = min(relative_index) - max(current_target_heap)
                            if temp_diff > 0 and temp_diff <= max_diff_second and temp_diff < max_diff_second:
                                max_diff_second = temp_diff
                                max_index = index

                        if max_index != -1:
                            self.heapInfoList[i + j].max_relative_heap_index = self.heapInfoList[i + j].relative_heap_index[max_index]
                            self.heapInfoList[i + j].max_relative_heap_inner_index = \
                            self.heapInfoList[i + j].relative_heap_inner_index[max_index]
                            current_target_heap = self.heapInfoList[i + j].max_relative_heap_index
                        else:
                            for index, relative_index in enumerate(self.heapInfoList[i + j].relative_heap_index):
                                temp_diff = min(relative_index) - max(current_target_heap)
                                if temp_diff >= -max_diff_second and temp_diff < 0 and temp_diff > max_diff_second:
                                    max_diff_second = temp_diff
                                    max_index = index
                            if max_index != -1:
                                self.heapInfoList[i + j].max_relative_heap_index = self.heapInfoList[i + j].relative_heap_index[
                                    max_index]

                                self.heapInfoList[i + j].max_relative_heap_inner_index = \
                                self.heapInfoList[i + j].relative_heap_inner_index[
                                    max_index]
                                current_target_heap = self.heapInfoList[i + j].max_relative_heap_index



    def resort_heap_index(self):
        '''对生成的heapIndex进行排序'''
        min_index = 0
        max_index = 0
        relative_indexs = []
        relative_heaps_indexs = []
        for i in range(len(self.heapInfoList)):
            current_heap = self.heapInfoList[i]

            if len(relative_indexs) == 0:
                relative_indexs.append(i)
                min_index = min(current_heap.max_relative_heap_index)
                max_index = max(current_heap.max_relative_heap_index)
                relative_heaps_indexs.append(current_heap.max_relative_heap_index)
            else:
                if (max(current_heap.max_relative_heap_index) >= min_index - self.grap_sencond  and max(current_heap.max_relative_heap_index)< min_index) \
                    or (min(current_heap.max_relative_heap_index) <= max_index + self.grap_sencond and min(current_heap.max_relative_heap_index)>  max_index):
                    relative_indexs.append(i)
                    relative_heaps_indexs.append(current_heap.max_relative_heap_index)
                    min_index = max(min(current_heap.max_relative_heap_index),min_index)
                    max_index = max(max(current_heap.max_relative_heap_index),max_index)

                else:
                    '''开始处理之前的heap堆'''
                    if len(relative_indexs) != 1:
                        sorted_heap_indexs = list(set([tuple(i) for i in relative_heaps_indexs]))
                        sorted_heap_indexs.sort(key=lambda x:x[0])
                        sorted_heap_current_count = 0

                        current_position_index_max = 0

                        for index, sub_index  in  enumerate(relative_indexs):
                            if index == 0:
                                self.heapInfoList[sub_index].max_relative_heap_index = sorted_heap_indexs[sorted_heap_current_count]
                                sorted_heap_current_count += 1
                                current_position_index_max = max(self.heapInfoList[sub_index].max_relative_heap_index)
                            else:
                                if tuple(self.heapInfoList[sub_index].max_relative_heap_index) not in sorted_heap_indexs:
                                    pass
                                else:
                                    if sub_index >= len(self.heapInfoList):
                                        j = 1
                                    self.heapInfoList[sub_index].max_relative_heap_index = sorted_heap_indexs[
                                        sorted_heap_current_count]
                                sorted_heap_current_count += 1

                    relative_indexs = []
                    relative_heaps_indexs = []
                    relative_indexs.append(i)
                    relative_heaps_indexs.append(current_heap.max_relative_heap_index)
                    min_index = max(min(current_heap.max_relative_heap_index), min_index)
                    max_index = max(max(current_heap.max_relative_heap_index), max_index)








                








